//===============================================================================
// Microsoft patterns & practices
// Web Client Software Factory
//-------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//-------------------------------------------------------------------------------
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================
using System;
using System.Data;
using System.Data.Common;
using System.Threading;
using Microsoft.Practices.EnterpriseLibrary.Data;
using WCSFContrib.PageFlow.Utils;
using WCSFContrib.PageFlow.Storage.EnterpriseLibrary.Properties;

namespace WCSFContrib.PageFlow.Storage.EnterpriseLibrary
{
	/// <summary>
	/// An implementation of an <see cref="IPageFlowInstanceStore">IPageFlowInstanceStore</see>
	/// that uses a the Enterprise Library Data Block <see cref="Database">Database</see> to manage 
	/// and store <see cref="IPageFlow">IPageFlow</see> instances.
	/// </summary>
	public class PageFlowInstanceCorrelationSqlProvider : IPageFlowInstanceStore
	{
        private Database _pageFlowStorage;
        private IPageFlowCorrelationTokenProvider _tokenProvider;		

		/// <overloads>
		/// Creates an instance of PageFlowInstanceCorrelationSqlProvider.
		/// </overloads>
		/// <summary>
		/// Creates an instance of PageFlowInstanceCorrelationSqlProvider using
		/// the provided database name.
		/// </summary>
		/// <remarks>
		/// The PageFlowInstanceCorrelationSqlProvider will use a 
		/// <see cref="CookiePageFlowCorrelationTokenProvider">CookiePageFlowCorrelationTokenProvider</see>
		/// to create and provide tokens for each instance.
		/// </remarks>
		/// <param name="databaseName">The name of the database to use as the store.</param>
		public PageFlowInstanceCorrelationSqlProvider(string databaseName)
			: this(databaseName, new CookiePageFlowCorrelationTokenProvider())
		{
		}

		/// <summary>
		/// Creates an instance of PageFlowInstanceCorrelationSqlProvider.
		/// </summary>
		/// <param name="databaseName">The name of the database to use as the store.</param>
		/// <param name="tokenProvider">The <see cref="IPageFlowCorrelationTokenProvider">IPageFlowCorrelationTokenProvider</see>
		/// implementation that will provide tokens.
		/// </param>
        /// 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
		public PageFlowInstanceCorrelationSqlProvider(string databaseName, IPageFlowCorrelationTokenProvider tokenProvider)
		{
			Guard.ArgumentNotNullOrEmptyString(databaseName, "databaseName");
			Guard.ArgumentNotNull(tokenProvider, "tokenProvider");
			_pageFlowStorage = DatabaseFactory.CreateDatabase(databaseName);
            _tokenProvider = tokenProvider;
		}

		/// <summary>
		/// Adds an <see cref="IPageFlow">IPageFlow</see> instance to the store.
		/// </summary>
		/// <param name="pageFlowInstance">The instance to add.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
        public void Add(IPageFlow pageFlowInstance)
		{
			Guard.ArgumentNotNull(pageFlowInstance, "pageFlowInstance");
			using (DbCommand command = _pageFlowStorage.GetStoredProcCommand("dbo.pageFlow_InsertInstance"))
			{
                string token = _tokenProvider.GetCorrelationToken();
                _pageFlowStorage.AddInParameter(command, "@instanceId", DbType.Guid, pageFlowInstance.Id);
				_pageFlowStorage.AddInParameter(command, "@pageFlowType", DbType.String,
				                                pageFlowInstance.Definition.PageFlowType.AssemblyQualifiedName);
                _pageFlowStorage.AddInParameter(command, "@correlationToken", DbType.String, token);
				_pageFlowStorage.AddInParameter(command, "@running", DbType.Boolean, false);
				_pageFlowStorage.ExecuteNonQuery(command);
			}
		}

		/// <overloads>
		/// Removes a PageFlow instance from the store.
		/// </overloads>
		/// <summary>
		/// Removes an <see cref="IPageFlow">IPageFlow</see> instance from the store.
		/// </summary>
		/// <param name="pageFlowInstance">The instance to remove.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
        public void Remove(IPageFlow pageFlowInstance)
		{
			Guard.ArgumentNotNull(pageFlowInstance, "pageFlowInstance");
			using (DbCommand command = _pageFlowStorage.GetStoredProcCommand("dbo.pageFlow_DeleteInstance"))
			{
				_pageFlowStorage.AddInParameter(command, "@instanceID", DbType.Guid, pageFlowInstance.Id);
				_pageFlowStorage.ExecuteNonQuery(command);
			}
		}

		/// <summary>
		/// Removes the <see cref="IPageFlow">IPageFlow</see> instance with the appropriate Guid from the store.
		/// </summary>
		/// <param name="id">The unique identifier of the instance to remove.</param>
		public void Remove(Guid id)
		{
			using (DbCommand command = _pageFlowStorage.GetStoredProcCommand("dbo.pageFlow_DeleteInstance"))
			{
				_pageFlowStorage.AddInParameter(command, "@instanceId", DbType.Guid, id);
				_pageFlowStorage.ExecuteNonQuery(command);
			}
		}

		/// <summary>
		/// Returns the <see cref="Guid">Guid</see> of the last instance.
		/// </summary>
		/// <returns>The unique identifier of the PageFlow instance.</returns>
		public Guid GetLastRunningInstance()
		{
			using (DbCommand command = _pageFlowStorage.GetStoredProcCommand("dbo.pageFlow_GetLastRunningInstanceByCorrelationToken"))
			{
                string token = _tokenProvider.GetCorrelationToken();
				_pageFlowStorage.AddInParameter(command, "@correlationToken", DbType.String, token);
				using (IDataReader reader = _pageFlowStorage.ExecuteReader(command))
				{
					if (reader.Read())
					{
						return reader.GetGuid(0);
					}
				}
			}
            return Guid.Empty;
		}

		/// <summary>
		/// Retrieves the unique identifier of an <see cref="IPageFlow">IPageFlow</see> instance 
		/// of the desired <see cref="Type">Type</see>.
		/// </summary>
		/// <param name="type">The <see cref="Type">Type</see> of PageFlow to retrieve.</param>
		/// <returns>The unique idenitifier of an instance of the desired type.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
        public Guid GetByType(Type type)
		{
			Guard.ArgumentNotNull(type, "type");
            using (DbCommand command = _pageFlowStorage.GetStoredProcCommand("dbo.pageFlow_GetInstanceByTypeAndByCorrelationToken"))
			{
                string token = _tokenProvider.GetCorrelationToken();
				_pageFlowStorage.AddInParameter(command, "@pageFlowType", DbType.String, type.AssemblyQualifiedName);
                _pageFlowStorage.AddInParameter(command, "@correlationToken", DbType.String, token);
				using (IDataReader reader = _pageFlowStorage.ExecuteReader(command))
				{
					if (reader.Read())
					{
						return reader.GetGuid(0);
					}
				}
			}
			return Guid.Empty;
		}

        /// <summary>
        /// Retrieves the instance <see cref="Type">Type</see> assembly qualified full name correspnding to an instance id
        /// </summary>
        /// <param name="id">The <see cref="Guid">Guid</see> of the instance</param>
        /// <returns>The assembly qualified name of the <see cref="Type">Type</see> of the instance or null if not found</returns>
        public string GetInstanceType(Guid id)
        {
            if (id == Guid.Empty)
                throw new ArgumentException(Resources.InstanceIdCannotBeEmpty, "id");

            using (DbCommand command = _pageFlowStorage.GetStoredProcCommand("dbo.pageFlow_GetTypeByInstanceId"))
            {
                _pageFlowStorage.AddInParameter(command, "@id", DbType.Guid, id);
                using (IDataReader reader = _pageFlowStorage.ExecuteReader(command))
                {
                    if (reader.Read())
                    {
                        return reader.GetString(0);
                    }
                }
            }
            return null;
        }

		/// <summary>
		/// Marks an <see cref="IPageFlow">IPageFlow</see> instance as the currently running instance.
		/// </summary>
		/// <param name="iPageFlow">The <see cref="IPageFlow">IPageFlow</see> instance to mark as running.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
        public void SetPageFlowRunning(IPageFlow iPageFlow)
		{
			Guard.ArgumentNotNull(iPageFlow, "iPageFlow");
			using (DbCommand command = _pageFlowStorage.GetStoredProcCommand("dbo.pageFlow_SetRunningInstanceForCorrelationToken"))
			{
                string token = _tokenProvider.GetCorrelationToken();
                _pageFlowStorage.AddInParameter(command, "@instanceID", DbType.Guid, iPageFlow.Id);
                _pageFlowStorage.AddInParameter(command, "@correlationToken", DbType.String, token);
				_pageFlowStorage.ExecuteNonQuery(command);
			}
		}

		/// <summary>
		/// Marks an <see cref="IPageFlow">IPageFlow</see> instance as not currently running.
		/// </summary>
		/// <param name="iPageFlow">The <see cref="IPageFlow">IPageFlow</see> instance to mark as not currently running.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Validation done by Guard class.")]
        public void SetPageFlowNotRunning(IPageFlow iPageFlow)
		{
			Guard.ArgumentNotNull(iPageFlow, "iPageFlow");
			using (DbCommand command = _pageFlowStorage.GetStoredProcCommand("dbo.pageFlow_ChangeInstanceStatus"))
			{
				_pageFlowStorage.AddInParameter(command, "@instanceID", DbType.Guid, iPageFlow.Id);
				_pageFlowStorage.AddInParameter(command, "@running", DbType.Boolean, false);
				_pageFlowStorage.ExecuteNonQuery(command);
			}
		}
    }
}
